function registerEvent(
  target: EventTarget,
  eventType: string,
  cb: (...args: any) => any,
) {
  target.addEventListener(eventType, cb);
  return {
    remove() {
      target.removeEventListener(eventType, cb);
    },
  };
}

function createHiddenIframe(
  target: HTMLElement,
  uri: string,
): HTMLIFrameElement {
  const iframe = document.createElement('iframe');
  iframe.src = uri;
  iframe.id = 'hiddenIframe';
  iframe.style.display = 'none';
  target.appendChild(iframe);

  return iframe;
}

/**
 * 通过创建iframe跳转来判断，safari用
 * @param uri
 */
function openUriWithHiddenFrame(uri: string) {
  return new Promise<boolean>((resolve) => {
    const timer = setTimeout(() => {
      resolve(false);
    }, 1000);

    let iframe = document.querySelector('#hiddenIframe') as HTMLIFrameElement;
    if (!iframe) {
      iframe = createHiddenIframe(document.body, 'about:blank');
    }

    function onBlur() {
      clearTimeout(timer);
      handler.remove();
      resolve(true);
    }
    const handler = registerEvent(window, 'blur', onBlur);

    iframe.contentWindow!.location.href = uri;
  });
}

/**
 * 通过失去焦点来判断
 * @param uri
 * @param timeout
 */
function openUriWithBlurHack(uri: string, timeout: number) {
  return new Promise<boolean | undefined>((resolve) => {
    // blur监听需要添加到顶层页面
    let target: Window = window;
    while (target !== target.parent) {
      target = target.parent;
    }
    const handler = registerEvent(target, 'blur', onBlur);

    function clear() {
      clearTimeout(timer1);
      // clearTimeout(timer2);
      handler.remove();
    }

    // can not detect
    const timer1 = window.setTimeout(() => {
      clear();
      resolve(undefined);
    }, timeout);

    // blur了即成功
    function onBlur() {
      clear();
      resolve(true);
    }

    // 全屏hack
    // let timer2 = 0;
    // if (document.documentElement.requestFullscreen) {
    //   try {
    //     await document.documentElement.requestFullscreen()

    //     // 成功调起后会自动退出全屏，所以根据50ms看看是否还是全屏状态来判定
    //     timer2 = setTimeout(() => {
    //       if (document.fullscreenElement) {
    //         document.exitFullscreen();
    //         clear();
    //         failCb();
    //       } else {
    //         clear();
    //         successCb();
    //       }
    //     }, 1000);  // 50ms不太够
    //   } catch (e) {
    //     // 全屏失败，fallback
    //   }
    // }

    try {
      window.location.href = uri;
    } catch (e) {
      clear();
      resolve(false);
    }
  });
}

/**
 * firefox用
 * @param uri
 */
function openUriUsingFirefox(uri: string) {
  return new Promise<boolean>((resolve) => {
    let iframe = document.querySelector(
      '#hiddenIframe',
    ) as HTMLIFrameElement | null;
    if (!iframe) {
      iframe = createHiddenIframe(document.body, 'about:blank');
    }

    try {
      iframe.contentWindow!.location.href = uri;
    } catch (e) {
      // if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
      resolve(false);
      // }
      return;
    }

    setTimeout(() => {
      try {
        // eslint-disable-next-line
        iframe!.contentWindow!.location.protocol;
      } catch (e) {
        resolve(false);
        return;
      }
      resolve(true);
    }, 50);
  });
}

/**
 * 用ms的专用方法检测，ie11专用
 * @param uri
 */
async function openUriWithMsLaunchUri(uri: string): Promise<boolean> {
  return new Promise((resolve) => {
    const timer = setTimeout(() => {
      clearTimeout(timer);
      resolve(true);
    }, 50);

    navigator.msLaunchUri?.(
      uri,
      () => {
        clearTimeout(timer);
        resolve(true);
      },
      () => {
        clearTimeout(timer);
        resolve(false);
      },
    );
  });
}

/**
 * 检测浏览器
 */
function checkBrowser() {
  const isOpera =
    'opera' in window || navigator.userAgent.indexOf(' OPR/') >= 0;
  const ua = navigator.userAgent.toLowerCase();
  return {
    isOpera,
    isFirefox: typeof (globalThis as any).InstallTrigger !== 'undefined',
    isSafari:
      (ua.indexOf('safari') >= 0 && ua.indexOf('chrome') < 0) ||
      Object.prototype.toString
        .call(window.HTMLElement)
        .indexOf('Constructor') > 0,
    isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream,
    isChrome: 'chrome' in window && !isOpera,
    isIE: /* @cc_on!@ */ false || 'documentMode' in document, // At least IE6
  };
}

interface DetectProtocalParam {
  uri: string;
  timeout?: number;
}

/**
 * 打开uri
 * @returns 是否打开成功，true/false表示探测到打开操作的成功/失败，undefined表示超时未能探测到
 */
export async function detectProtocal(
  param: DetectProtocalParam,
): Promise<boolean | undefined> {
  const { uri } = param;
  const timeout = param.timeout || 10000; // default 10s

  // ie11
  if (typeof navigator.msLaunchUri === 'function') {
    // for IE and Edge in Win 8 and Win 10
    const result = await openUriWithMsLaunchUri(uri);
    return result;
  }

  const browser = checkBrowser();

  let result: boolean | undefined;
  if (browser.isFirefox) {
    result = await openUriUsingFirefox(uri);
  } else if (browser.isChrome || browser.isIOS) {
    result = await openUriWithBlurHack(uri, timeout);
  } else if (browser.isSafari) {
    result = await openUriWithHiddenFrame(uri);
  } else {
    // not supported, implement please
    result = undefined;
  }
  return result;
}

export default detectProtocal;
